AWS SAM テンプレートに既存リソースをインポートする手順を確認する
ちょっとしたサーバレスアプリケーションを作るとき、AWS SAM は便利です。
AWS SAM のテンプレートは AWS CloudFormation テンプレートの拡張機能なので、リソースのインポートも簡単にできると思ったのですが、ひと手間必要だったのでその手順を紹介したいと思います。
背景
- AWS SAM で Lambda 関数を作成する際、テンプレートに記載しなくても CloudWatch Logs のロググループが作成されます。
- テンプレートに記載しない場合、このロググループは AWS SAM のリソースとして管理されないので、スタックを削除してもロググループが残り続けます。
- AWS SAM の管理対象ではないリソースに対して、変更を加えたい場合は別の手段で行う必要があり、管理面で非効率です。
- CloudFormation のようにリソースをインポートして、関連リソースを管理できるようにしたいですよね。
※ 今回は、CloudWatch Logs を対象にしていますが、リソースのインポート方法自体は他のリソースでも変わらないので、作業手順の参考にしていただければと思います。
サンプルの Lambda 関数を AWS SAM で作成する
最初に AWS SAM で単純な 「Lambda 関数だけ」 を作成します。
sam init \ --runtime python3.9 \ --name blog-test-function \ --app-template hello-world \ --package-type Zip
cd blog-test-function
template.yaml
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: > blog-test-function Sample SAM Template for blog-test-function Globals: Function: Timeout: 3 Resources: BlogTestFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello_world/ Handler: app.lambda_handler Runtime: python3.9 Architectures: - x86_64 AutoPublishAlias: dev
- Lambda 関数
- Lambda の動作は今回の件とは関係ないので、コードの中身は何でも構いません。
import json def lambda_handler(event, context): return { "statusCode": 200, "body": json.dumps({ "message": "hello world", }), }
- ビルド & デプロイ
sam build
sam package \ --output-template-file packaged.yaml \ --s3-bucket [アーティファクト用の S3 バケット名] sam deploy \ --template-file packaged.yaml \ --stack-name blog-sam-import-stack \ --s3-bucket [アーティファクトがある S3 バケット名] \ --capabilities CAPABILITY_NAMED_IAM \ --no-fail-on-empty-changeset
CloudWatch Logs のログは Lambda 関数が実行されないと生成されないので、一度 Lambda 関数を実行しておきます。
Lambda 関数を実行することで CloudWatch のロググループが生成されて、ログストリームができていることが確認できます。
今回デフォルトで生成されたロググループの名前は、/aws/lambda/blog-sam-import-stack-BlogTestFunction-6qrGlr8YueM0
となっていました。
ロググループの名前の構造は、/aws/lambda/<Lambda 関数の名前>
となっています。Lambda のコンソールから確認してみると関数と同じ名前であることが分かります。
SAM テンプレートを使ってインポートしてみる(失敗するパターン)
次に、AWS SAM テンプレートに CloudWatch Logs のロググループをインポートしてみたいと思います。
CloudFormation 単体でリソースをインポートする手順として考えると、SAM テンプレートをそのままインポートすればいいように思います。しかし、この方法ではインポートに失敗します。
試しに先程作成した SAM テンプレート(template.yaml
)をインポートしてみます。
そのまま「Next」をクリックして進めます。
AWS SAM で使った template.yaml
をアップロードします。
ファイルをアップロードすると画像のようにエラーとなってしまいました。
これは、CloudFormation コンソールでは AWS SAM で利用される AWS::Serverless
トランスフォームセクションがサポートされないためです。詳細は下記に記載されていますので、参考にして下さい。
サポート対象の一覧は下記に記載があります。
リソースインポートの手順
AWS SAM のテンプレートをそのまま使ってインポートすることはできないことが分かりました。
AWS SAM のテンプレートにリソースをインポートするには、下記の作業を実施します。
- インポートしたいリソースを追記した CloudFormation テンプレートファイル
template.yml
を用意 - インポートするリソース情報を記載したファイル(リソースインポートファイル)
import.txt
を作成 - 上記2つのファイルを使って、AWS CLI で CloudFormation の「変更セット」を作成
- 作成された「変更セット」を適用してスタックにインポート
公式の情報では次のページが参考になります。(先程掲載したものと同じです)
CloudFormation テンプレートを用意
最初に、インポートしたいリソースを追記したテンプレートを用意します。
すでに AWS SAM テンプレートがありますが、これは CloudFormation のテンプレートを拡張したものなので、純粋な CloudFormation テンプレートとして用意します。
用意するといってもゼロから作成する必要は無く、すでにデプロイ済みのスタックから取得しましょう。CloudFormation のコンソールからコピペします。
このテンプレートに、CloudWatch Logs のリソースを追記したものが下記になります。
25 〜 29 行目に追記しています。
(15 行目の CodeUri
は環境に応じて異なるので伏せ字にしています。)
このファイルを適当な作業フォルダに template.yml
として保存しておきます。
このファイルは、リソースをインポートするために使うものなので、AWS SAM で使用した template.yaml
とは別ファイルとして作成して下さい。
27 行目に記載している通り、インポートするリソースは、CFnテンプレート内で 「DeletionPolicy」属性を指定する必要がある点に注意して下さい。
また、LogGroupName
で指定しているロググループの名前は、インポートしたい既存のグループ名になるようにしておきます。
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: 'blog-test-function Sample SAM Template for blog-test-function ' Globals: Function: Timeout: 3 Resources: BlogTestFunction: Type: AWS::Serverless::Function Properties: CodeUri: s3://xxxxx/xxxxxx Handler: app.lambda_handler Runtime: python3.9 Architectures: - x86_64 AutoPublishAlias: dev Metadata: SamResourceId: BlogTestFunction # インポートするリソースを追記 LambdaFuncLogGroup: Type: AWS::Logs::LogGroup DeletionPolicy: Retain Properties: LogGroupName: !Sub /aws/lambda/${BlogTestFunction}
リソースインポートファイルを用意
次に、下記のような内容でリソースインポートファイルを作成します。
ResourceType
: インポートしたいリソースのリソースタイプ- 今回は
AWS::Logs::LogGroup
- 今回は
LogicalResourceId
: インポートしたいリソースの論理ID- 先程の
template.yml
でLambdaFuncLogGroup
としているのでコレを指定
- 先程の
ResourceIdentifier
: インポートしたいリソースの識別名- インポートしたいリソースである CloudWatch Logs のロググループ名を指定
この内容で、import.txt
というファイル名で 先程のtemplate.yml
と同じ作業ディレクトリに保存します。
[ { "ResourceType": "AWS::Logs::LogGroup", "LogicalResourceId": "LambdaFuncLogGroup" , "ResourceIdentifier": { "LogGroupName":"/aws/lambda/blog-sam-import-stack-BlogTestFunction-6qrGlr8YueM0" } } ]
ここまでで、作業ディレクトリには以下の 2 ファイルがある状態になっています。
template.yml
import.txt
変更セットの作成
ファイルの準備ができたので、CloudFormation の「変更セット」を作成します。 変更セットの作成では、作成した2つのファイルを利用するので、AWS CLI で行います。 これまでと同じ作業ディレクトリで下記のコマンドを実行します。
--stack-name
にはインポートしたいスタックの名前を指定します。
aws cloudformation create-change-set \ --stack-name blog-sam-import-stack \ --change-set-name import-test-set \ --resources-to-import file://import.txt \ --change-set-type IMPORT \ --template-body file://template.yml \ --capabilities CAPABILITY_IAM
これで変更セットが無事作成できました。
上記の 「import-test-set」 をクリックして確認すると、追加したロググループが表示されていることが分かります。
変更セットを適用してスタックにインポート
最後にこの「変更セット」を実行してロググループをインポートします。
(ロググループは DeletionPolicy: Retain
と指定しているのでリソースは作成されず、指定のスタックにインポートされるという訳です)
そのまま「Execute change set」をクリックして実行します。
スタックの「Event」タブで状態が 「IMPORT_COMPLETE」 になればインポート完了です。
次の画像ように、CloudWatch のロググループのタグ情報も更新されて、CloudFormation(AWS SAM)の管理対象になったことが確認できました。
インポート前は何もタグがありません。
インポート後は3つのタグがセットされています。
AWS SAM からスタックを更新して確認してみる
これでリソースのインポートができたので、AWS SAM を使って対象のロググループの設定を更新してみましょう。
AWS SAM のテンプレートを下記のように更新します。(25 〜 29 行目)
ロググループのリソースを追加していますが、RetentionInDays: 30
も記載している点がポイントです。
デフォルトで作成されたロググループはログの保持期限の設定が無く「無期限」になっています。インポートしたリソースが AWS SAM の管理対象になっていれば、「1ヶ月」に更新されることを期待したものになります。
(インポート用の template.yml
と同様に、LogGroupName
で指定しているロググループの名前をインポートされた既存のグループ名になるようにしておきましょう。)
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: > blog-test-function Sample SAM Template for blog-test-function Globals: Function: Timeout: 3 Resources: BlogTestFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello_world/ Handler: app.lambda_handler Runtime: python3.9 Architectures: - x86_64 AutoPublishAlias: dev # CloudWatch Logs for Lambda # 明示的にCloudWatch Logsを作成してretentionを指定 LambdaFuncLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub /aws/lambda/${BlogTestFunction} RetentionInDays: 30
一応、変更前の保持期限を確認してみます。
aws logs describe-log-groups \ --log-group-name-prefix \ /aws/lambda/blog-sam-import-stack-BlogTestFunction-6qrGlr8YueM0 \ |jq '{Retention: .logGroups[].retentionInDays}'
null
で無期限となっていますね。
{ "Retention": null }
ちなみに、AWS CLI では null
と表示されますが、コンソール上では Never expire
と表示されます。
この状態で、AWS SAM を使ってスタックを更新してから再度、保持期間を確認してみます。
aws logs describe-log-groups \ --log-group-name-prefix \ /aws/lambda/blog-sam-import-stack-BlogTestFunction-6qrGlr8YueM0 \ |jq '{Retention: .logGroups[].retentionInDays}'
正しく「1ヶ月」に変わりました。
{ "Retention": 30 }
以上で、AWS SAM のテンプレートに既存リソースをインポートできたことが確認できました。
最後に
AWS SAM でも簡単にリソースのインポートができるかと思いましたが、意外と手間がかかることが分かりました。
手順を事前に確認しておけば、何かあったときに慌てずに対応できますね。
以上です。